﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Discovery;
using System.Xml;
using System.Xml.Linq;
using DirectoryService.ServiceModel.Discovery;
using Devtm.ServiceModel.Discovery.Collection;
using System.ServiceModel.Channels;

namespace Devtm.ServiceModel.Discovery
{


    public class ServiceDirectoryStore : DirectoryStore
    {


        TableCollection<DiscoveryItem, string> onlineServices;
        ViewCollection<DiscoveryItem, string> NotifyDirectoryChangedView;

        private volatile object _lock = new object();


        public ServiceDirectoryStore()
        {

            // Annuaire de service
            this.onlineServices = new TableCollection<DiscoveryItem, string>(c => c.Key);

            // filtre sur l'annuaire pour ne selectionner que les services online qui implémentent 'INotifyDirectoryChanged'
            NotifyDirectoryChangedView = new ViewCollection<DiscoveryItem, string>(this.onlineServices, c => this.IsNotifyDirectoryChanged(c));

            // trigger sur l'annuaire qui se declenche quand un service (Add/Remove)
            this.onlineServices.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(onlineServices_CollectionChanged);

        }



        void onlineServices_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {


            e.ApplyChange<EndpointDiscoveryMetadata>
                (

                 // Add
                 f =>
                 {

                 },

                 // Del
                 f =>
                 {

                 },

                 // Reset
                 () => { }

                );


            /*
             * 
            string contextFilter1 = metadata.GetContextFilter();

            
            foreach (var item in NotifyDirectoryChangedView)
            {

                // si on a des abonnements a l'annuaire, il faut les notifier qu'un nouveau service s'est inscrit.
                // Pour cela on utilise le filtre pour ne notifier que ceux qui sont interessés.

                string contextFilter2 = item.GetContextFilter();

                if (contextFilter1 == contextFilter2)
                {

                    // On récupère la conf
                    Binding binding = metadata.GetBinding();

                    // on créer un wrapper
                    INotifyDirectoryChanged srv = ServiceFactory.Instance.Get<INotifyDirectoryChanged>(binding, metadata.Address);

                    // On notifie
                    srv.DirectoryChanged();

                }


            }
            */

        }


        /// <summary>
        /// Adds the service
        /// </summary>
        /// <param name="datas">The datas.</param>
        public void AddOnline(EndpointDiscoveryMetadata datas)
        {

            PrintDiscoveryMetadata(datas, "Adding");

            string key = datas.Address.Uri.AbsoluteUri;

            lock (_lock)

                if (!this.onlineServices.ContainsKey(key))
                    this.onlineServices.Add(new DiscoveryItem() { Key = key, Metadata = datas });
                else
                {
                    DiscoveryItem m = this.onlineServices[key] as DiscoveryItem;                       
                    m.Metadata = datas;
                }
                

        }


        /// <summary>
        /// Removes the service
        /// </summary>
        /// <param name="datas">The datas.</param>
        public void RemoveOnline(EndpointDiscoveryMetadata datas)
        {

            PrintDiscoveryMetadata(datas, "Removing");

            lock (_lock)
                this.onlineServices.RemoveKey(datas.Address.Uri.AbsoluteUri);

        }


        /// <summary>
        /// Matches from online service
        /// </summary>
        /// <param name="criteria">The criteria.</param>
        /// <returns></returns>
        public EndpointDiscoveryMetadata MatchFromOnlineService(ResolveCriteria criteria)
        {

            EndpointDiscoveryMetadata matchingEndpoint = null;

            lock (_lock)
                foreach (DiscoveryItem item in this.onlineServices)
                    if (criteria.Address == item.Metadata.Address)
                        matchingEndpoint = item.Metadata;

            return matchingEndpoint;

        }


        /// <summary>
        /// Async Matches from online service
        /// </summary>
        /// <param name="findRequestContext">The find request context.</param>
        public void MatchFromOnlineService(FindRequestContext findRequestContext)
        {
            
            lock (_lock)

                foreach (DiscoveryItem item in this.onlineServices)
                    if (findRequestContext.Criteria.IsMatch(item.Metadata))
                    {

                        TransformXElement translator = GetTransformXElement(item.Metadata);

                        if (translator != null)
                            findRequestContext.AddMatchingEndpoint(translator.Translate(item.Metadata));
                        else
                            findRequestContext.AddMatchingEndpoint(item.Metadata);

                    }

        }


        private TransformXElement GetTransformXElement(EndpointDiscoveryMetadata meta)
        {
            return null;
        }


        private bool IsNotifyDirectoryChanged(DiscoveryItem metada)
        {


            Extension e = metada.Extension;

            return true;

            //XmlQualifiedName c = endpointDiscoveryMetada.ContractTypeNames[0];
            //bool retour = c.Namespace == Constantes.DirectoryNamespace && c.Name == "INotifyDirectoryChanged";
            //return retour;
        }


    }

}
